读书笔记-深入分析 Java Web 技术内幕(二)

第二部分: Java 基础知识

1. Javac 编译原理

Java 源代码经历词法分析器、语法分析器、语义分析,代码生成最终转换为字节码

词法分析器: Scanner(扫描全文件)、Lexer(识别符合 Java 语法规范的 Token 序列)

语法分析器: AST(抽象语法树)

语义分析器: 让 AST 的信息更加完善, 包含了: 映射符号表、处理注解、处理标注(@Unchecked), 检查变量合法性、数据流分析(还是各种语法检查)、语义分析(优化代码逻辑, 消除无用代码, 解除语法糖)

代码生成器: 遍历语法树, 生成字节码, 写入 class 文件

以上 4 个环节都会对语法树进行不同额处理操作, 采用的设计模式是访问者模式

想要操作 Java 源码: 业内 Eclipse JDT

2. 深入 class 文件结构

有两种翻译 class 的工具:

  1. Oolong 是 JVM 的汇编语言, 使用方式: java COM.sootNsmoke.oolong.Gnoloo Message.class 生成 Message.j 文件

  2. 通过 JDK 自带的 javap, 使用方式: javap -verbose Message > message.txt

3. 深入分析 ClassLoader 工作机制
  1. 将 class 加载到 JVM 中
  • defineClass(byte[], int, int): 将字节流解析成 JVM 能够识别的 Class 对象

  • findClass(String): 类的加载规则实现于此

  • loadClass(String): 获取类的对象

  • resolveClass(Class<?>): 让 JVM 加载类的同时并链接

  1. 审查每个类应该由谁加载: 父优先的等级加载机制
  • Bootstrap ClassLoader: 主要加载 JVM 自身工作需要的类, 这个 ClassLoader 完全是由 JVM 控制, 没有子类, 参数: -Xbootclasspath:

  • ExtClassLoader: 用于加载目标为 System.getProperty(“java.ext.dirs”) 的类, 是 AppClassLoader 的父类, 参数:
    -Djava.ext.dirs

  • AppClassLoader: 用于加载 classpath 的类, 也是所有自实现的类加载器的父类, 参数: -Djava.class.path=

  1. 重解析: 将字节码重新解析成统一要求的对象格式
  • 字节码验证, Class类数据结构分析及、相应的内存分配、最后的符号表的链接

  • 类中的静态属性和初始化赋值, 静态代码块的执行

常见的类加载错误分析

  • ClassNotFoundException

检查当前 classpath 目录下有没有指定的文件存在, 用 this.getClass().getClassLoader().getResource(“”).toString() 获取 classpath 路径

  • NoClassDefFoundError

使用 new 关键字、属性引用某个类、继承了某个接口或类、方法的某个参数中引用了某个类时, 触发 JVM 隐式加载这些类发现不存在

Tomcat 的 ClassLoader 层级

  • ExtClassLoader -> AppClassLoader -> StandardClassLoader -> WebappClassLoader

加载 Tomcat 容器本身仍然是 AppClassLoader, 在 WebAppClassLoader 中增加了缓存, 取代了加载时优先查找 JVM 的 findLoaderClass 缓存

类的热部署原理: 创建不同的 ClassLoader 实例对象, 然后通过这个不同的实例对象类加载同名的类

4. JVM 体系结构与工作方式

CPU 是跟指令集挂钩的, 顾名思义就是计算机能识别的机器语言, 如 RISC, CISC, MMX 等, 来自于 Intel 和 AMD

JVM 也有用一套指令集, 俗称 JVM 字节码指令集, 用来执行 class 的字节码

JVM 结构组成:

  • 类加载器

  • 执行引擎(负责执行 class 文件中包含的字节码指令, 相当于实际机器上的 CPU, 目标是内存区的栈)

    • SUN 的 Hotspot 是基于栈的执行引擎

    • Google 的 Dalvik 是基于寄存器的执行引擎

  • 内存区(内存管理: 方法区、Java 堆、栈、PC寄存器、本地方法区)

  • 本地方法调用(调用 C/C++ 实现本地方法的代码返回结果)

在执行引擎技术里面衍生了 JIT: 在Java编程语言和环境中,即时编译器(JIT compiler,just-in-time compiler)是一个把 Java 的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序

5. JVM 内存管理

Java 涉及到需要分配的内存有: 堆(-Xmx 和 -Xms), 线程(为其分配个堆栈), 类和类加载器(存活于 PermGen 区), NIO(直接使用本地内存), JNI(加载实现的类库)

在 Java 虚拟机规范中将 Java 运行时数据划分为 6 种, 分别为: PC 寄存器, Java 栈(为线程分配的空间), 堆(线程共享), 方法区(存储的是类结构信息, 类加载器加载完就存放与此, 线程共享, 永久区中), 本地方法区(为 Native 方法准备的空间), 运行时常量池(存放每个 class 文件中的常量表, 隶属于方法区的一部分)

根可达算法决定了哪些对象是垃圾, 垃圾回收算法决定了怎么回收: 基于分代的GC(Young, Old, Perm 区依次递进)

Serial Collector 算法 & Parallel Collector 算法 & CMS Collector 算法

GC 日志参数:

-verbose:gc 可以辅助输出一些详细的 GC 信息

-XX:+PrintGCDetails 输出 GC 详细信息

-XX:+PrintGCApplicationStoppedTime 输出 GC 造成应用程序暂停的时间

-XX:+PrintGCDateStamps GC 发生的时间信息

-XX:+PrintHeapAtGC 在 GC 前后输出堆中各个区域的大小

如需转载,请注明出处